﻿using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Shipeng.Util;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using System.Linq;
using YNTK.Common.Const;
using YNTK.Common.Enum;
using YNTK.Common.Filter;
using YNTK.Common.Util;
using YNTK.Dependency;
using YNTK.DynamicApiController;
using YNTK.FriendlyException;
using YNTK.System.Entitys.Dto.Common;
using YNTK.System.Entitys.Dto.Menu;
using YNTK.System.Entitys.Menu;
using YNTK.System.Interfaces.Permission;
using YNTK.System.Entitys.Dto.Permission.Role;
using YNTK.System.Entitys.Role;
using YNTK.Mapster;

namespace YNTK.System.Service
{
    /// <summary>
    /// 菜单管理
    /// 版 本：V1.0.0
    /// 版 权：云南通科科技有限公司（https://www.yntkkj.com）
    /// 作 者：李仕鹏
    /// 日 期：2022-10-03 
    /// </summary>
    [ApiDescriptionSettings(Tag = "菜单管理", Name = "Menu", Order = 795)]
    [Route("api/[controller]")]
    public class MenuService : IMenuService, IDynamicApiController, ITransient
    {
        private readonly ISqlSugarRepository<MenuEntity> _menuRepository;//菜单仓储
        private readonly ISqlSugarRepository<TkRoleEntity> _tkRoleRepository;//角色仓储
        private readonly ISqlSugarRepository<TkRoleMenuRelevancyEntity> _tkRoleMenuRelevancyRepository;//角色菜单关联仓储       
        private readonly HttpContext _httpContext;

        /// <summary>
        /// 构造方法依赖注入
        /// </summary>
        /// <param name="menuRepository">菜单仓储</param>
        /// <param name="tkRoleRepository">角色仓储</param>
        /// <param name="tkRoleMenuRelevancyRepository">角色菜单关联仓储</param>
        public MenuService(ISqlSugarRepository<MenuEntity> menuRepository,
            ISqlSugarRepository<TkRoleEntity> tkRoleRepository,
            ISqlSugarRepository<TkRoleMenuRelevancyEntity> tkRoleMenuRelevancyRepository)
        {
            _menuRepository = menuRepository;
            _tkRoleRepository = tkRoleRepository;
            _tkRoleMenuRelevancyRepository = tkRoleMenuRelevancyRepository;
            _httpContext = App.HttpContext;
        }

        /// <summary>
        /// 获取菜单管理权限(按钮)数据
        /// </summary>
        /// <returns></returns>
        [HttpGet("getauthoritys")]
        public TkResult<List<EnumListDto>> GetAuthorityList()
        {
            Dictionary<int, string> dic = EnumHelper.EnumToDic<ButtonTypeEnum>();
            var result = new List<EnumListDto>();
            foreach (KeyValuePair<int, string> kv in dic)
            {
                result.Add(new EnumListDto {Id=kv.Key.ToStr(), Value = kv.Key, Description = kv.Value });
            }
            return TkResult<List<EnumListDto>>.Success(result);
        }

        /// <summary>
        /// 获取菜单树结构数据
        /// </summary>
        /// <param name="query">查询菜单请求模型</param>
        /// <returns></returns>
        [HttpPost("gettree")]
        public async Task<TkResult<List<MenuTreeDto>>> GetTree([FromBody] QueryMenuFilterRequestModel query)
        {
            try
            {
                var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
                string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
                //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
                var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
                if (authenticationType == "4" || authenticationType == "5")
                    throw YNTKException.Oh("暂无菜单权限");

                List<string> menuIds = null;
                List<TkRoleMenuRelevancyEntity> orgPermissions = null;
                if (authenticationType=="2" || authenticationType == "3")
                {
                    orgPermissions = await GetOrganizePermissionsAsync();
                    if(orgPermissions == null || orgPermissions.Count<=0)
                        return TkResult<List<MenuTreeDto>>.Success(null);
                    menuIds = orgPermissions.Select(r => r.MenuId).Distinct().ToList();
                }

                var data = await _menuRepository.Entities
                .WhereIF(menuIds != null && menuIds.Count>0,r=> menuIds.Contains(r.Id))
                .WhereIF(query.type!=null,r=>r.Type==query.type)
                .WhereIF(query.enabledMark==1,r=>r.EnabledMark==1)
                .WhereIF(query.enabledMark == 0, r => r.EnabledMark != 1)
                .WhereIF(!string.IsNullOrWhiteSpace(query.keyword),r=>r.Title.Contains(query.keyword) ||
                r.Code.Contains(query.keyword) || r.Description.Contains(query.keyword) || 
                r.Route.Contains(query.keyword))
                .Where(r => r.DeleteMark == null)
                .OrderBy(r => r.SortCode).ToListAsync();
                var treeList = data.Adapt<List<MenuTreeDto>>();
                if(treeList!=null && treeList.Count > 0)
                {
                    var permissions = GetAuthorityList().data;
                    treeList.FindAll(r=>!string.IsNullOrWhiteSpace(r.authoritys))
                        .ForEach(item => {
                            if (authenticationType == "2" || authenticationType == "3")
                            {
                                item.authoritys = orgPermissions?
                                .FirstOrDefault(r => r.MenuId == item.id)?.Authoritys??"";
                            }
                            if(!string.IsNullOrWhiteSpace(item.authoritys))
                            {
                                item.authoritysData =(from it in permissions
                                .Where(p => item.authoritys.Split(',').ToList().Contains(p.Value.ToStr()))
                                .ToList()
                                select new EnumListDto() {Id=item.id,Value=it.Value,Description=it.Description}
                                ).ToList();
                                item.authoritysText = string.Join(",",
                                item.authoritysData.Select(r=>r.Description).ToArray());
                            }
                        });
                }
                treeList = treeList.ToTree().OrderBy(r=>r.sortCode).ToList();
                return TkResult<List<MenuTreeDto>>.Success(treeList);
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("[")) throw YNTKException.Oh($"{ex.Message}");
                throw YNTKException.Oh($"获取菜单树结构数据发生异常：{ex.Message}");
            }
        }

        /// <summary>
        /// 添加菜单
        /// </summary>
        /// <param name="input">添加菜单请求模型</param>
        /// <returns></returns>
        [HttpPost("add")]
        public async Task Add([FromBody] AddMenuRequestModel input)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            if (authenticationType != "1") throw YNTKException.Oh("暂无添加菜单权限");
            if (await _menuRepository.AnyAsync(m => m.Title == input.title && m.Type==input.type && m.ParentId==input.parentId && m.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D4000);
            if (await _menuRepository.AnyAsync(m => m.Code == input.code && m.Type == input.type && m.ParentId == input.parentId && m.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D4008);
            var entity = input.Adapt<MenuEntity>();
            var isOk = await _menuRepository.Context.Insertable(entity)
                .CallEntityMethod(m => m.Creator()).ExecuteReturnEntityAsync();
            _ = isOk ?? throw YNTKException.Oh(ErrorCode.D4009);
        }

        /// <summary>
        /// 修改菜单
        /// </summary>
        /// <param name="input">修改菜单请求模型</param>
        /// <returns></returns>
        [HttpPut("update")]
        public async Task Update([FromBody] UpdateMenuRequestModel input)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            if (authenticationType != "1") throw YNTKException.Oh("暂无修改菜单权限");
            var entity = await _menuRepository.FirstOrDefaultAsync(r=>r.Id==input.id && r.DeleteMark==null);
            _ = entity ?? throw YNTKException.Oh(ErrorCode.D4011);
            if (await _menuRepository.AnyAsync(m => m.Title == input.title && m.Type == input.type && m.ParentId == input.parentId && m.Id != input.id && m.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D4000);
            if (await _menuRepository.AnyAsync(m => m.Code == input.code && m.Type == input.type && m.ParentId == input.parentId && m.Id != input.id && m.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D4008);
            //var entity = input.Adapt<MenuEntity>();
            entity = input.Mapper(entity);
            entity.ParentId = input.parentId;
            entity.LastModifyTime = DateTime.Now;
            entity.LastModifyUserId = user.Value;
            var isOK = await _menuRepository.Context.Updateable(entity)
                   .WhereColumns(it => new { it.Id }).ExecuteCommandAsync();
            if (!(isOK > 0))
                throw YNTKException.Oh(ErrorCode.D3012);
        }

        /// <summary>
        /// 删除菜单
        /// </summary>
        /// <param name="id">菜单id</param>
        /// <returns></returns>
        [HttpDelete("delete/{id}")]
        public async Task Delete([Required] string id)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            if (authenticationType != "1") throw YNTKException.Oh("暂无删除组织权限");
            var entity = await _menuRepository.SingleAsync(m => m.Id == id && m.DeleteMark == null);
            _ = entity ?? throw YNTKException.Oh(ErrorCode.D4011);
            var isOk = await _menuRepository.DeleteAsync(r=>r.Id==id);
            if (!(isOk > 0))
                throw YNTKException.Oh(ErrorCode.D4012);
        }

        /// <summary>
        /// 更新菜单状态(启用/禁用)
        /// </summary>
        /// <param name="id">菜单id</param>
        /// <returns></returns>
        [HttpPut("state/{id}")]
        public async Task UpdateState([Required] string id)
        {
            var userId = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID)?.Value;
            if (!await _menuRepository.AnyAsync(m => m.Id == id && m.DeleteMark == null))
                throw YNTKException.Oh(ErrorCode.D4011);
            var isOk = await _menuRepository.Context.Updateable<MenuEntity>().SetColumns(it => new MenuEntity()
            {
                EnabledMark = SqlFunc.IIF(it.EnabledMark == 1, 0, 1),
                LastModifyUserId = userId,
                LastModifyTime = SqlFunc.GetDate()
            }).Where(it => it.Id == id).ExecuteCommandAsync();
            if (!(isOk > 0))
                throw YNTKException.Oh(ErrorCode.D4013);
        }




        /// <summary>
        /// 获取组织管理员菜单
        /// </summary>
        /// <returns></returns>
        private async Task<List<TkRoleMenuRelevancyEntity>> GetOrganizePermissionsAsync()
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
            var role = await _tkRoleRepository.FirstOrDefaultAsync(r => r.OrganizeId == organizeId &&
            r.Administrator && !string.IsNullOrWhiteSpace(r.Identification) && r.DeleteMark == null);
            if(role == null)return null;
            var data = await _tkRoleMenuRelevancyRepository.Entities
                .Where(r => r.TkRoleId == role.Id).ToListAsync();
            return data;
        }


        #region PublicMethod
        /// <summary>
        /// 根据菜单id集合获取菜单数据
        /// </summary>
        /// <param name="ids">菜单id集合</param>
        /// <returns></returns>
        [NonAction]
        public async Task<List<MenuEntity>> GetListAsync(List<string> ids)
        {
            if (ids == null || ids.Count <= 0) return null;
            var result = await _menuRepository.Entities.Where(m =>
            ids.Contains(m.Id) && m.DeleteMark == null).OrderBy(m=>m.SortCode).ToListAsync();
            return result;
        }
        #endregion

    }
}
